#region File Description
//  DomoBCI 
//  License: GNU/GPL
//  Developer: Federico Leonardo Motta
//  File description: This component handles the Comunication between HIM and this software.
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Net.Sockets;
using System.Net;
using System.Text;
using DomoBCI.Core.Comunication;
using DomoBCI.Core.GameManager;
using SharedContent;
using System.Threading;
#endregion


namespace DomoBCI.Core.GameManager
{
    public delegate void HimEventHandler();
    public class ComunicationManager: Microsoft.Xna.Framework.GameComponent, IComunicationManager
    {
        static Boolean isRunning = false;

        static private Socket server;
        static private IPEndPoint ip;
        private int size = 1024;
        protected BCIMessage bci;
        public event HimEventHandler MessageReceive;
        private byte[] data = new byte[1024];
        public int kind;
        public int value;
        public int[] buffer;
        public int bufferSize;
        bool paramDone = false;

        public ComunicationManager(Game game)
            : base(game)
        {
            game.Services.AddService(typeof(IComunicationManager), this);
        }

        protected override void Dispose(bool disposing)
        {
            Game.Services.RemoveService(typeof(IComunicationManager));
            base.Dispose(disposing);
        }

        public override void Update(GameTime gameTime)
        {
            /*if(server != null){
                if (server.Available != 0) {
                   ReadBuffer();
                }
            }*/
            base.Update(gameTime);
        }

        public void HIMCreateUser(User usr){
            if (!isRunning)
            {
                if (!HIMConnect()) { return; }
                isRunning = true;
            }
            // 2) invio user
            bci = new BCIMessage();
            int[] nullo = new int[16];
            byte[] sendb = new byte[76];
            byte[] buffer = new byte[4];
            nullo[0] = usr.Id;
            for(int x=1; x<=usr.Nome.Length;x++){
                nullo[x] = (int)(usr.Nome.ElementAt(x-1));
            }
            bci.message = bci.ComposeMessage(DomoBCI.Core.Comunication.BCIMessage.CREATE_USER, 0, nullo, 0);
            sendb = bci2.utils.utils.BCIMessageToByte(bci.message);
            server.Send(sendb); 
            }

        public void HIMDeleteUser(int id)
        {
            if (!isRunning)
            {
                if (!HIMConnect()) { return; }
                isRunning = true;
            }
            // 2) invio user
            bci = new BCIMessage();
            int[] nullo = new int[16];
            byte[] sendb = new byte[76];
            byte[] buffer = new byte[4];
            nullo[0] = id;
            bci.message = bci.ComposeMessage(DomoBCI.Core.Comunication.BCIMessage.DELETE_USER, 0, nullo, 0);
            sendb = bci2.utils.utils.BCIMessageToByte(bci.message);
            server.Send(sendb);
        }

        public void HIMLogin(int id)
        {
            if (!isRunning)
            {
                if (!HIMConnect()) { return; }
                isRunning = true;
            }
            // 2) invio user
            bci = new BCIMessage();
            int[] nullo = new int[16];
            byte[] sendb = new byte[76];
            byte[] buffer = new byte[4];
            nullo[0] = id;
            bci.message = bci.ComposeMessage(DomoBCI.Core.Comunication.BCIMessage.LOGIN, 0, nullo, 0);
            sendb = bci2.utils.utils.BCIMessageToByte(bci.message);
            server.Send(sendb);
        }

        public void HIMScreening(int freq) {
            if (!isRunning)
            {
                if (!HIMConnect()) { return; }
                isRunning = true;
            }
            // 2) invio user
            bci = new BCIMessage();
            int[] nullo = new int[16];
            byte[] sendb = new byte[76];
            byte[] buffer = new byte[4];
            nullo[0] = freq;
            bci.message = bci.ComposeMessage(DomoBCI.Core.Comunication.BCIMessage.GRAPH_TRIG, nullo[0], nullo, 0);
            sendb = bci2.utils.utils.BCIMessageToByte(bci.message);
            server.Send(sendb);
        }

        public void HIMRequestParams() {
            if (!isRunning)
            {
                if (!HIMConnect()) {  }
                isRunning = true;
            }
            
            short[] freq = new short[4];
            // 1) invio REQUEST_PARAMS
            bci = new BCIMessage();
            int[] nullo = new int[16];
            byte[] sendb = new byte[76];
            byte[] buffer = new byte[4];
            bci.message = bci.ComposeMessage(DomoBCI.Core.Comunication.BCIMessage.REQUEST_PARAMS, 0, nullo, 0);
            sendb = bci2.utils.utils.BCIMessageToByte(bci.message);
            server.Send(sendb);
            Thread.Sleep(1000);
            server.BeginReceive(data, 0, 1024, SocketFlags.None, new AsyncCallback(ReceiveData), server);
        }

        public void HIMStartAcquisition()
        {
            if (!isRunning)
            {
                if (!HIMConnect()) { return; }
                isRunning = true;
            }
            // 1) richiesta start
            bci = new BCIMessage();
            int[] nullo = new int[16];
            byte[] sendb = new byte[76];
            byte[] buffer = new byte[4];
            bci.message = bci.ComposeMessage(DomoBCI.Core.Comunication.BCIMessage.START_ACQ, 0, nullo, 0);
            sendb = bci2.utils.utils.BCIMessageToByte(bci.message);
            server.Send(sendb);

            /*//2) attesa risposta: CONNECTION_COMPLETED
                        byte[] data = new byte[1024];
                        int bytesReceived;
                        string str = "ads";
                        while (str[0] != 103)
                        {

                            bytesReceived = server.Receive(data, data.Length, SocketFlags.None);
                            str = Encoding.UTF8.GetString(data, 0, data.Length);

                        }
                        Console.WriteLine("HIM response:\t" + (int)str[0]);*/
        }


        public void ReceiveData(IAsyncResult iar)
        {
            int value;
            short[] buffer;
            int bufferSize;

            Socket remote = (Socket)iar.AsyncState;
            int recv = remote.EndReceive(iar);
            string stringData = Encoding.ASCII.GetString(data, 0, recv);
            //I primi quattro byte sono int KIND
            int kind = System.BitConverter.ToInt32(data, 0);
            Console.WriteLine("HIM response kind:\t" + kind);
            switch (kind)
            {
                case BCIMessage.CLASSIFICATION:
                    value = System.BitConverter.ToInt32(data, 4);
                   /*  switch (value)
                    {
                       case 16:
                            direction = utilities.direction.up;
                            dirUsed = false;
                            break;
                        case 32:
                            direction = utilities.direction.down;
                            dirUsed = false;
                            break;
                        case 1:
                            direction = utilities.direction.left;
                            dirUsed = false;
                            break;
                        case 8:
                            direction = utilities.direction.right;
                            dirUsed = false;
                            break;
                        case 4:
                            direction = utilities.direction.none;
                            dirUsed = false;
                            break;
                    }*/
                    break;
                case BCIMessage.FEEDBACK:
                    value = System.BitConverter.ToInt32(data, 4);
                    buffer = new short[4];
                    for (int i = 0; i < 4; i++)
                        buffer[i] = System.BitConverter.ToInt16(data, (8 + i * 4));
                    bufferSize = System.BitConverter.ToInt32(data, recv - 4);
                    break;
                case BCIMessage.SET_PARAMS:
                    value = System.BitConverter.ToInt32(data, 4);
                    buffer = new short[4];
                    for (int i = 0; i < 4; i++)
                        buffer[i] = System.BitConverter.ToInt16(data, (8 + i * 4));
                    StateManager.freq = buffer;
                    bufferSize = System.BitConverter.ToInt32(data, (8 + 4 * 4));
                    paramDone = true;
                    //led.setPeriods(buffer, 4);
                    break;
                case BCIMessage.CONNECTION_COMPLETED:
                    //led.start();
                    break;
            }
            remote.BeginReceive(data, 0, 1024, SocketFlags.None, new AsyncCallback(ReceiveData), remote);
        }

       public void ReadBuffer(){
           server.BeginReceive(data, 0, 1024, SocketFlags.None, new AsyncCallback(ReceiveData), server);
        }
        
        public Boolean HIMConnect() {
            // 1) apro un socket sulla porta 3001
            ip = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3001);
            //ip = new IPEndPoint(IPAddress.Parse("131.175.137.73"), 3001);
            server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                server.Connect(ip);
                return true;
            }
            catch (SocketException)
            {
                return false;
            }
        }
    }
}
